home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac-Source 1994 July
/
Mac-Source_July_1994.iso
/
C and C++
/
Libraries
/
TurboTCP 1.0.1
/
MiniTelnet.source
/
CTelnetTerminal.cp
< prev
next >
Wrap
Text File
|
1993-12-10
|
16KB
|
746 lines
/*
** CTelnetTerminal.cp
**
** MiniTelnet application
** Telnet terminal session document
**
** Copyright © 1993, FrostByte Design / Eric Scouten
**
*/
#include "CTelnetTerminal.h"
#ifndef TurboTCPHeaders
#include <Packages.h>
#include <Global.h>
#include <Commands.h>
#include <TCLUtilities.h>
#include <TBUtilities.h>
#include <CApplication.h>
#include <CDesktop.h>
#include <CBartender.h>
#include <CClipboard.h>
#include <CDecorator.h>
#include <CScrollPane.h>
#include <CWindow.h>
#endif
#include <CFile.h>
#include "CTCPDriver.h"
#include "CTCPStream.h"
#include "CTCPResolverCall.h"
#include "CTerminalPane.h"
// global TCL objects
extern CApplication *gApplication;
extern CBartender *gBartender;
extern CClipboard *gClipboard;
extern CDecorator *gDecorator;
extern CDesktop *gDesktop;
// global TurboTCP objects
extern CTCPDriver *gTCPDriver;
// —— initialization ——
/*______________________________________________________________________
**
** ITelnetTerminal
**
** Initialize the Telnet terminal session document.
**
** aSupervisor (CApplication *): the document’s supervisor
** printable (Boolean): TRUE if document is printable
** recBufferSize (long): size of the receive buffer we need
** theDefaultPort (b_16): default IP port number
** autoReceiveSize (long): number of entries in RDS for auto-receive,
** 0 to disable autoreceiving
** autoReceiveNum (short): number of auto-receive calls to issue at once
** must be at least 1
**
*/
void CTelnetTerminal::ITelnetTerminal (CApplication *aSupervisor, Boolean printable,
long recBufferSize, b_16 theDefaultPort,
short autoReceiveSize, short autoReceiveNum)
{
itsTerminal = NULL;
itsTermMode = 0;
CTelnetInterpreter::ITelnetInterpreter(aSupervisor, printable, recBufferSize,
theDefaultPort, autoReceiveSize, autoReceiveNum);
showFileName = FALSE;
}
// —— creating new sessions ——
/*______________________________________________________________________
**
** NewSession
**
** Create a new window for document. Pulls information from the settings record which
** it receives and opens a session accordingly.
**
** theSettings (TelnetSettingsRec *): the settings record to use
**
*/
void CTelnetTerminal::NewSession (TelnetSettingsRec *theSettings)
{
// copy the settings record
BlockMove(theSettings, &r, sizeof (TelnetSettingsRec));
BlockMove(&r.hostName, &hostCName, 256);
goAwayOnClose = r.closeOnSessionEnd;
showDebug = r.showDebug;
// build a window immediately
BuildWindow();
// open host by name
#if _TestTerminal
itsTerminal->DoWriteStr("No TCP session. Just type onto terminal.\r\n");
#else
itsStream->SetULPTimeoutValue(20);
OpenUserHost((char *) &r.hostName, defaultPort, TRUE);
#endif
}
/*______________________________________________________________________
**
** BuildWindow
**
** Build a window for a terminal connection.
**
*/
void CTelnetTerminal::BuildWindow (void)
{
CScrollPane *theScrollPane;
CTerminalPane *theMainPane;
Rect sizeRect;
// create a window
itsWindow = new(CWindow);
itsWindow->IWindow(WINDTelnet, FALSE, gDesktop, this);
// set maximum size of window
SetRect(&sizeRect, 100, 100, sizeX+16, sizeY+16);
itsWindow->SetSizeRect(&sizeRect);
// create the scrolling pane
theScrollPane = new(CScrollPane);
theScrollPane->IScrollPane(itsWindow, this, 10, 10, 0, 0,
sizELASTIC, sizELASTIC,
TRUE, TRUE, TRUE);
theScrollPane->FitToEnclFrame(TRUE, TRUE);
// create the main view pane
theMainPane = new(CTerminalPane);
theMainPane->ITerminalPane(theScrollPane, this, 0, 0, 0, 0,
sizELASTIC, sizELASTIC);
itsMainPane = theMainPane;
itsTerminal = theMainPane;
itsGopher = theMainPane;
theMainPane->FitToEnclosure(TRUE, TRUE);
// make sure the scroll pane knows about the panorama
theScrollPane->InstallPanorama(theMainPane);
// place, size, and title the window
gDecorator->PlaceNewWindow(itsWindow);
AutoTitle();
itsWindow->Select();
}
/*______________________________________________________________________
**
** AutoTitle
**
** Sets title for window. Turns on or off cursor blinking depending on whether session
** is established.
**
*/
void CTelnetTerminal::AutoTitle (void)
{
CTCPSessionDoc::AutoTitle();
itsTerminal->SetBlinking(sessionReady);
}
// —— command/event handling ——
/*______________________________________________________________________
**
** DoCommand
**
** Handle all commands document can understand.
**
** theCommand (long): command number that was issued
**
*/
void CTelnetTerminal::DoCommand (long theCommand)
{
char addrStr [18]; // used for send IP addr command
switch (theCommand) {
// Edit menu: support pasting TEXT
case cmdPaste:
DoPaste();
break;
// Telnet menu: send various strings to server
case cmdSendSynch:
if (showDebug)
PrintDebugStr("{Urgent IAC DM}");
itsStream->SetNextUrgent(FALSE);
itsStream->SendCString("\377\362");
break;
case cmdSendBreak:
if (showDebug)
PrintDebugStr("{Urgent IAC BRK IAC DM}");
itsStream->SetNextUrgent(FALSE);
itsStream->SendCString("\377\363\377\362");
break;
case cmdSendAO:
if (showDebug)
PrintDebugStr("{Urgent IAC AO IAC DM}");
itsStream->SetNextUrgent(FALSE);
itsStream->SendCString("\377\365\377\362");
break;
case cmdSendIP:
if (showDebug)
PrintDebugStr("{Urgent IAC IP IAC DM}");
itsStream->SetNextUrgent(FALSE);
itsStream->SendCString("\377\364\377\362");
break;
case cmdSendAYT:
if (showDebug)
PrintDebugStr("{Urgent IAC AYT IAC DM}");
itsStream->SetNextUrgent(FALSE);
itsStream->SendCString("\377\366\377\362");
break;
case cmdSendGA:
if (showDebug)
PrintDebugStr("{IAC GA}");
itsStream->SendCString("\377\371");
break;
case cmdSendEC:
if (showDebug)
PrintDebugStr("{IAC EC}");
itsStream->SendCString("\377\367");
break;
case cmdSendEL:
if (showDebug)
PrintDebugStr("{IAC EL}");
itsStream->SendCString("\377\370");
break;
case cmdSendIPAddr:
itsResolver->DoAddrToStr(gTCPDriver->GetIPAddr(),
(char *) &addrStr);
itsStream->SendCString((char *) &addrStr);
break;
case cmdShowDebug:
showDebug = !showDebug;
r.showDebug = showDebug;
break;
// not ours, send along the chain
default:
inherited::DoCommand(theCommand);
}
}
/*______________________________________________________________________
**
** DoKeyDown
**
** Process key-down events. Parses CR, LF codes and generates the
** standard CR/LF sequence when CR is passed and ignores LF.
**
** theChar (char): the character that was entered
** keyCode (byte): the Mac ADB key number for the key that was pressed
** macEvent (EventRecord *): the entire event record
**
*/
void CTelnetTerminal::DoKeyDown (char theChar, Byte keyCode, EventRecord *macEvent)
{
// make sure this isn’t an errant command key
if (!((*macEvent).modifiers & cmdKey)) {
if (theChar != charLF) {
#if _TestTerminal
HandleNVTChar(theChar);
if (theChar == charCR)
HandleNVTChar(charLF);
#else
if (!sessionReady) {
SysBeep(0);
return;
}
if (theChar == charBS)
itsStream->SendChar(r.backspaceChar);
else
itsStream->SendChar(theChar);
if (theChar == charCR)
itsStream->SendChar(charLF);
#endif
itsTerminal->ScrollToSelection();
}
}
}
/*______________________________________________________________________
**
** DoAutoKey
**
** Process automatic (repeated) key-down events.
**
** theChar (char): the character that was entered
** keyCode (byte): the Mac ADB key number for the key that was pressed
** macEvent (EventRecord *): the entire event record
**
*/
void CTelnetTerminal::DoAutoKey (char theChar, Byte keyCode, EventRecord *macEvent)
{
DoKeyDown(theChar, keyCode, macEvent); // boy, wasn’t that simple!
}
/*______________________________________________________________________
**
** DoPaste
**
** Respond to Paste command by sending all characters from the
** clipboard to the remote host.
**
*/
void CTelnetTerminal::DoPaste (void)
{
Handle theData;
char lineBfr[83];
char *p, *pl, *lb;
long rem;
short i;
// get the clipboard contents
if (!(gClipboard->GetData('TEXT', &theData)))
return;
HLock(theData);
// send it in line or 80-char chunks
p = *theData;
rem = GetHandleSize(theData);
while (rem > 0) {
// fetch a line (or 81 chars)
i = 0;
pl = p;
lb = &lineBfr[0];
while ((rem-(i) > 0) && (*pl != charCR) && (i < 80))
i++, *lb++ = *pl++;
if (*pl == charCR) {
i++, *pl++;
*lb++ = charCR;
*lb++ = charLF;
}
*lb = '\0';
// send the line
#if _TestTerminal
HandleNVTLine((char *) &lineBfr);
#else
itsStream->SendBfrCpy((char *) &lineBfr, cstrlen(lineBfr));
#endif
rem -= i;
p += i;
}
HUnlock(theData);
DisposHandle(theData);
}
/*______________________________________________________________________
**
** UpdateMenus
**
** Enable Telnet-specific commands.
**
*/
void CTelnetTerminal::UpdateMenus (void)
{
CDocument::UpdateMenus();
// enable “Paste” command only if valid Clipboard
#if _TestTerminal
if (gClipboard->DataSize('TEXT'))
gBartender->EnableCmd(cmdPaste);
#else
if (sessionReady && (gClipboard->DataSize('TEXT')))
gBartender->EnableCmd(cmdPaste);
#endif
// enable “Send…” commands only if session is active
if (sessionReady) {
gBartender->EnableCmd(cmdSendSynch);
gBartender->EnableCmd(cmdSendBreak);
gBartender->EnableCmd(cmdSendAO);
gBartender->EnableCmd(cmdSendIP);
gBartender->EnableCmd(cmdSendAYT);
gBartender->EnableCmd(cmdSendGA);
gBartender->EnableCmd(cmdSendEC);
gBartender->EnableCmd(cmdSendEL);
gBartender->EnableCmd(cmdSendIPAddr);
gBartender->EnableCmd(cmdShowDebug);
gBartender->CheckMarkCmd(cmdShowDebug, showDebug);
}
}
// —— data handling methods ——
/*______________________________________________________________________
**
** HandleNVTChar
**
** Process a character which was recieved over Telnet for the network virtual terminal.
**
** theChar (char): the character which was received
**
*/
void CTelnetTerminal::HandleNVTChar (char theChar)
{
itsTerminal->DoWriteChar(theChar);
}
/*______________________________________________________________________
**
** HandleNVTLine
**
** Process a line (or <81 chars) which was recieved over Telnet for the network virtual
** terminal.
**
** theLine (char *): the line which was received (C string)
**
*/
void CTelnetTerminal::HandleNVTLine (char *theLine)
{
itsTerminal->DoWriteStr(theLine);
}
// —— Telnet command handling ——
/*______________________________________________________________________
**
** ReceivedDo
**
** Respond to a Telnet [IAC DO option] sequence.
**
** theOption (uChar): the option code
**
*/
void CTelnetTerminal::ReceivedDo (uchar theOption)
{
uchar respondStr[4];
// parse the option and respond to it
switch (theOption) {
// accept terminal type option only
case optTERMINAL_TYPE:
respondStr[0] = charIAC;
respondStr[1] = escWILL;
respondStr[2] = theOption;
respondStr[3] = '\0';
if (showDebug) {
PrintDebugStr("{IAC WILL");
PrintDebugCharNum(theOption, ' ', '}');
}
itsStream->SendCString((char *) &respondStr);
break;
// reject the remaining options
default:
CTelnetInterpreter::ReceivedDo(theOption);
}
}
/*______________________________________________________________________
**
** ReceivedAYT
**
** Respond to a Telnet [IAC AYT] sequence.
**
*/
void CTelnetTerminal::ReceivedAYT (void)
{
if (showDebug)
PrintDebugStr("{Yes}");
itsStream->SendCString("[Yes]");
}
/*______________________________________________________________________
**
** ReceivedEC
**
** Respond to a Telnet [IAC EC] sequence.
**
*/
void CTelnetTerminal::ReceivedEC (void)
{
itsTerminal->DoEraseChar();
}
/*______________________________________________________________________
**
** ReceivedEL
**
** Respond to a Telnet [IAC EL] sequence.
**
*/
void CTelnetTerminal::ReceivedEL (void)
{
itsTerminal->DoEraseLine();
}
/*______________________________________________________________________
**
** ReceivedSE
**
** Parse subnegotiation parameters. Called when [IAC SE] is received.
**
*/
void CTelnetTerminal::ReceivedSE (void)
{
// dispatch to an option-parsing routine
if (sbBfrIndex < 1)
return;
switch (sbBfr[0]) {
case optTERMINAL_TYPE:
OptionTerminalType();
break;
default:
CTelnetInterpreter::ReceivedSE();
}
}
// —— Telnet option handling ——
/*______________________________________________________________________
**
** OptionTerminalType
**
** Interpret an [IAC SB TERMINAL-TYPE ... IAC SE] sequence. Default method always says
** this is an unknown terminal.
**
*/
void CTelnetTerminal::OptionTerminalType (void)
{
uchar respondStr [47];
short i = 2;
// respond only to TERMINAL-TYPE SEND queries; ignore others
if (sbBfr[1] == 1) {
// build a response string
respondStr[0] = charIAC;
respondStr[1] = escSB;
respondStr[2] = optTERMINAL_TYPE;
// switch to next terminal emulation
itsTermMode++;
if (itsTermMode > termMax)
itsTermMode = 0;
// SetTerminalMode(itsTermMode);
// get name of terminal emulation
GetTerminalName(itsTermMode, &respondStr[3]);
// display response if debugging mode enabled
if (showDebug) {
PrintDebugStr("{IAC SB TERM-TYPE IS ");
PrintDebugStr((char *) &respondStr[3]);
PrintDebugStr(" IAC SE}");
}
// add end of SB string & send it
while (respondStr[++i])
;
respondStr[i] = charIAC;
respondStr[i+1] = escSE;
respondStr[i+2] = '\0';
itsStream->SendCString((char *) &respondStr);
}
}
// —— terminal emulation handling ——
/*______________________________________________________________________
**
** GetTerminalName
**
** Return the Internet assigned name for the terminal being used.
**
** termIndex (short): the terminal emulation number
** termStr (uchar *): buffer to receive the terminal name (max 40 chars)
**
*/
void CTelnetTerminal::GetTerminalName (short termIndex, uchar *termStr)
{
uchar theTerm[41] = "UNKNOWN"; // for now, all we have is “UNKNOWN”
BlockMove(&theTerm, termStr, 41);
}
/*______________________________________________________________________
**
** —— debugging methods ——
**
** These methods were useful to me in debugging the implementation of various Telnet
** options. They are enabled by the “Show debugging codes” option of the settings dialog.
**
*/
/*______________________________________________________________________
**
** PrintDebugStr
**
** Write a string to the terminal for debugging purposes.
**
** theDebugStr (char *): the string to write
**
*/
void CTelnetTerminal::PrintDebugStr (char *theDebugStr)
{
itsTerminal->DoWriteStr(theDebugStr);
}
/*______________________________________________________________________
**
** PrintDebugCharNum
**
** Print a character number for debugging. Provided as a debugging routine. The number
** is bracketed by the two characters indicated, i.e. if you call PrintDebugCharNum('!', '[', ']'),
** you get [33] written to the terminal.
**
** theChar (char): the character number to write
** leftBracket (char): prefix to character number
** rightBracket (char): suffix to character number
**
*/
void CTelnetTerminal::PrintDebugCharNum (char theChar, char leftBracket, char rightBracket)
{
itsTerminal->DoWriteCharNum(theChar, leftBracket, rightBracket);
}